﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEditor;

namespace Nirvana
{
    [DisallowMultipleComponent]
    [RequireComponent(typeof(Renderer))]
    [ExecuteInEditMode]
    [AddComponentMenu("Nirvana/Render/Nirvana Renderer")]
    public class NirvanaRenderer : MonoBehaviour
    {

        [SerializeField]
        [Tooltip("The origin material used for the renderer.")]
        private Material[] materials;

        [SerializeField]
        [Tooltip("The render queue.")]
        private int renderQueue = -1;

        [Tooltip("Whether show the renderer bounds")]
        [SerializeField]
        private bool showBounds;

        private ShaderKeywords shaderkeywords;
        private Bounds[] bounds;


        private LinkedListNode<NirvanaRenderer> nirvanarender_list;
        private static LinkedList<RenderEffectManager> renderEffectMgrList = new LinkedList<RenderEffectManager>();
        private MaterialPropertyBlock materialPropertyBlock;
        private Material[] allMaterials;
        /// <summary>
        /// 对象状态：可以移动，固定
        /// </summary>
        private bool isStatic;
        private Renderer _renderer;

        public Material[] Materials
        {
            get
            {
                return this.materials;
            }
            set
            {
                this.materials = value;
            }
        }

        public Bounds[] CustomBounds
        {
            get
            {
                return this.bounds;
            }
        }

        public int RenderQueue
        {
            get
            {
                return this.renderQueue;
            }
            set
            {
                this.renderQueue = value;
            }
        }

        public Renderer UnityRenderer
        {
            get
            {
                if (this._renderer == null)
                {
                    this._renderer = base.GetComponent<Renderer>();
                }
                return this._renderer;
            }
        }
        public bool IsStatic
        {
            get
            {
                return this.isStatic;
            }
            set
            {
                this.isStatic = value;
            }
        }
        public MaterialPropertyBlock PropertyBlock
        {
            get
            {
                if (this.materialPropertyBlock == null)
                {
                    this.materialPropertyBlock = new MaterialPropertyBlock();
                }
                return this.materialPropertyBlock;
            }
        }

        private void Awake()
        {
            this.OnWillRenderObject();
        }

        private void OnEnable()
        {
            if (this.bounds != null && this.nirvanarender_list == null)
            {
                this.nirvanarender_list = Singleton<CullingManager>.Instance.RegisterRenderer(this);
            }
        }
        private void OnDisable()
        {
            if (this.nirvanarender_list != null)
            {
                Singleton<CullingManager>.Instance.UnregisterRenderer(this.nirvanarender_list);
                this.nirvanarender_list = null;
            }
        }
        private void OnDestroy()
        {
            if (this.nirvanarender_list != null)
            {
                Singleton<CullingManager>.Instance.UnregisterRenderer(this.nirvanarender_list);
                this.nirvanarender_list = null;
            }
        }

#if UNITY_EDITOR
        private void OnValidate()
        {
            PrefabType prefabType = PrefabUtility.GetPrefabType(base.gameObject);
            if (prefabType == PrefabType.Prefab)
            {
                if (this._renderer != null)
                {
                    this._renderer.sharedMaterials = this.materials;
                }
            }
            else
            {
                this.OnWillRenderObject();
            }
        }

        private void OnDrawGizmosSelected()
        {
            if (this.showBounds)
            {
                Color color = Gizmos.color;
                Gizmos.color = Color.cyan;
                if (this.bounds == null)
                {
                    Bounds bounds = this.UnityRenderer.bounds;
                    Gizmos.DrawWireCube(bounds.center, bounds.size);
                }
                else
                {
                    foreach (Bounds bounds2 in this.bounds)
                    {
                        Gizmos.DrawWireCube(bounds2.center, bounds2.size);
                    }
                }
                Gizmos.color = color;
            }
        }
#endif


        private Material[] GetAllMaterials()
        {
            if(this.allMaterials == null || this.allMaterials.Length != materials.Length)
            {
                this.allMaterials = new Material[materials.Length];
            }
            return this.allMaterials;
        }

        public RenderEffect[] FindAllEffect()
        {
            List<RenderEffect> list = new List<RenderEffect>();
            foreach (var p in renderEffectMgrList)
            {
                p.FindEffects(this, list);
            }
            return list.ToArray();
        }

#if UNITY_EDITOR
        public void SetDefaultMaterial()
        {
            if (this.materials == null)
            {
                this.materials = new Material[0];
            }
            if (this.materials.Length == 0)
            {
                Renderer component = base.GetComponent<Renderer>();
                if (component != null && component.sharedMaterial != null)
                {
                    ArrayUtility.Add<Material>(ref this.materials, component.sharedMaterial);
                    return;
                }
            }
        }

        public void AddDefaultMaterial()
        {
            if (this.materials == null)
            {
                this.materials = new Material[0];
            }
            if (this.materials.Length == 0)
            {
                Renderer component = base.GetComponent<Renderer>();
                if (component != null && component.sharedMaterial != null)
                {
                    ArrayUtility.Add<Material>(ref this.materials, component.sharedMaterial);
                    return;
                }
            }
            ArrayUtility.Add<Material>(ref this.materials, null);
        }
#endif
        public void ClearPropertyBlock()
        {
            this.materialPropertyBlock = null;
        }

        public void SetKeyword(int keyword)
        {
            this.shaderkeywords.SetKeyword(keyword);
        }
        public void UnsetKeyword(int keyword)
        {
            this.shaderkeywords.UnsetKeyword(keyword);
        }

        public void SetCustomBounds(Bounds[] bounds)
        {
            this.bounds = bounds;
            if (this.nirvanarender_list == null)
            {
                this.nirvanarender_list = Singleton<CullingManager>.Instance.RegisterRenderer(this);
            }
        }

        public void ClearCustomBounds()
        {
            this.bounds = null;
            if (this.nirvanarender_list != null)
            {
                Singleton<CullingManager>.Instance.UnregisterRenderer(this.nirvanarender_list);
                this.nirvanarender_list = null;
            }
        }

        public static LinkedListNode<RenderEffectManager> AddRendererEffectMgr(RenderEffectManager mgr)
        {
            return renderEffectMgrList.AddLast(mgr);
        }

        internal static void RemoveRendererEffectMgr(LinkedListNode<RenderEffectManager> node)
        {
            renderEffectMgrList.Remove(node);
        }

        private void OnWillRenderObject()
        {
            if (this.materials == null)
            {
                return;
            }
            ShaderKeywords mshaderkeywords = this.shaderkeywords;
            LinkedListNode<RenderEffectManager> next;
            for (var linkedListNode = renderEffectMgrList.First; linkedListNode != null; linkedListNode = next)
            {
                next = linkedListNode.Next;
                RenderEffectManager value = linkedListNode.Value;
                value.SetupRendererStart(this, ref mshaderkeywords, ref this.materialPropertyBlock);
            }
            if(this.renderQueue !=-1 || (mshaderkeywords !=null  && !mshaderkeywords.IsEmpty))
            {
                var allMats = GetAllMaterials();
                for(int i = 0; i < this.materials.Length; i++)
                {
                    var mat = this.materials[i];
                    if (mat != null) allMats[i] = Singleton<RendererMaterialCache>.Instance.CreateMaterial(mat, this.renderQueue, mshaderkeywords);
                    else allMats[i] = null;
                }
                this.UnityRenderer.sharedMaterials = allMats;
            }
            else
            {
                this.UnityRenderer.sharedMaterials = this.materials;
            }

            if(this.materialPropertyBlock != null)
            {
                this.UnityRenderer.SetPropertyBlock(this.materialPropertyBlock);
            }
        }





    }
}

